package org.xtext.tp5.mydsl.jvmmodel

import com.google.inject.Inject
import org.eclipse.xtext.common.types.JvmDeclaredType
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.util.IAcceptor
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.xtext.tp5.mydsl.myDsl.Entity
import org.xtext.tp5.mydsl.myDsl.Operation
import org.xtext.tp5.mydsl.myDsl.Property
import org.xtext.tp5.mydsl.myDsl.Variable
import org.xtext.tp5.mydsl.myDsl.Value
import org.eclipse.emf.common.util.EList
import org.eclipse.xtext.common.types.JvmVisibility
import org.eclipse.xtext.common.types.JvmField

/**
 * <p>Infers a JVM model from the source model.</p> 
 *
 * <p>The JVM model should contain all elements that would appear in the Java code 
 * which is generated from the source model. Other models link against the JVM model rather than the source model.</p>     
 */
class MyDslJvmModelInferrer extends AbstractModelInferrer {

    /**
     * convenience API to build and initialize JVM types and their members.
     */
	@Inject extension JvmTypesBuilder
	
	@Inject extension IQualifiedNameProvider

	/**
	 * The dispatch method {@code infer} is called for each instance of the
	 * given element's type that is contained in a resource.
	 * 
	 * @param element
	 *            the model to create one or more
	 *            {@link org.eclipse.xtext.common.types.JvmDeclaredType declared
	 *            types} from.
	 * @param acceptor
	 *            each created
	 *            {@link org.eclipse.xtext.common.types.JvmDeclaredType type}
	 *            without a container should be passed to the acceptor in order
	 *            get attached to the current resource. The acceptor's
	 *            {@link IJvmDeclaredTypeAcceptor#accept(org.eclipse.xtext.common.types.JvmDeclaredType)
	 *            accept(..)} method takes the constructed empty type for the
	 *            pre-indexing phase. This one is further initialized in the
	 *            indexing phase using the closure you pass to the returned
	 *            {@link org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor.IPostIndexingInitializing#initializeLater(org.eclipse.xtext.xbase.lib.Procedures.Procedure1)
	 *            initializeLater(..)}.
	 * @param isPreIndexingPhase
	 *            whether the method is called in a pre-indexing phase, i.e.
	 *            when the global index is not yet fully updated. You must not
	 *            rely on linking using the index if isPreIndexingPhase is
	 *            <code>true</code>.
	 */
   	def dispatch void infer(Entity element, IAcceptor<JvmDeclaredType> acceptor, boolean isPrelinkingPhase) {
   		// Here you explain how your model is mapped to Java elements, by writing the actual translation code.
   		
   		acceptor.accept(element.toClass(element.fullyQualifiedName) [
   			documentation = element.documentation
   			for (trait : element.traitTypes) {
   				for (operation : trait.operations) {
   					members += operation.toMethod(operation.name, operation.type) [
   						for (p : operation.params) {
   								parameters += p.toParameter(p.name, p.parameterType)
   						}
   						documentation = operation.documentation
   						body = operation.body
   						setVisibility(JvmVisibility::getByName(visibility.getName))
   					]
   				}
   			}  			
   			for (feature : element.features) {
   				switch feature {
   					Property : {
   						switch feature {
   							Variable : {
   								members += feature.toField(feature.name, feature.type) [
   									setVisibility(JvmVisibility::getByName(visibility.getName))
   								]
   								
   								members += feature.toSetter(feature.name, feature.type)
   								members += feature.toGetter(feature.name, feature.type)
   							}
   							Value : {
   								members += feature.toField(feature.name, feature.type) [
   									setVisibility(JvmVisibility::getByName(visibility.getName))   									
   								]
   								
   								members += feature.toGetter(feature.name, feature.type)
   							}
   						}
   					}
   					Operation : {
   						members += feature.toMethod(feature.name, feature.type) [
   							for (p : feature.params) {
   								parameters += p.toParameter(p.name, p.parameterType)
   							}
   							documentation = feature.documentation
   							body = feature.body
   							setVisibility(JvmVisibility::getByName(visibility.getName))
   						]
   					}
   				}
   			}	
   		])
   	}

}

